<?php
/**************************************************************************
	ESAM (EVE to SMF API Module)
	Copyright (C) 2009  Clinton Weiss

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
**************************************************************************/

if (!defined('SMF'))
{
	require(dirname(__FILE__) . '/../SSI.php');
}

$debug_level = ( isset($_GET['verbosity']) ? $_GET['verbosity'] : 0);
if ($debug_level != 0)
{
	isAllowedTo(array('admin_forum'));
}

function debug($verbosity, $message)
{
	global $debug_level;
	if ($debug_level != 0 && $verbosity <= $debug_level)
	{
		print $message;
	}
}

include_once "EsamAlliance.php";
include_once "EsamMembers.php";
include_once "EsamQuery.php";
include_once "EsamEveApi.php";
include_once "EsamGlobal.php";
include_once "EsamRules.php";
include_once "EsamSkillDump.php";

if (isset($_GET['cronjob']))
{
	$esamValidator = new EsamValidator();
	$esamValidator->validateApiKeys();
}

class EsamValidator
{
	private $esamGlobal = null;

	private $includeCorpTicker = false;

	private $alliance_xml = null;

	function getAllianceXml()
	{
		if (empty($this->alliance_xml))
		{
			$ea = new EsamEveApi();
			$data = $ea->getAllianceList();
			$this->alliance_xml = new SimpleXMLElement($data);
		}
		return $this->alliance_xml;
	}

	function setVerbosity($verbose_level)
	{
		global $debug_level;
		$debug_level = $verbose_level;
	}

	/**
	 * Validates all stored API keys
	 */
	// TODO: break this apart into smaller methods
	function validateApiKeys($validate_member_id = null)
	{
		global $scripturl;

		$startTime = $this->getTime();
		$multipleKeyAuthentication = $validate_member_id == null;

		$resultSet = null;
		if (empty($validate_member_id))
		{
			$esamQuery = new EsamQuery();
			$esamQuery->setQuery($esamQuery->Q_EVE_API_LIST);
			$resultSet = $esamQuery->getResultSet();
		} else
		{
			$esamQuery = new EsamQuery();
			$esamQuery->setQuery($esamQuery->Q_EVE_API);
			$esamQuery->setParameter("member_id", $validate_member_id);
			$resultSet = $esamQuery->getResultSet();
		}

		// Prepare Global
		$this->esamGlobal = new EsamGlobal();
		$this->includeCorpTicker = $this->esamGlobal->getGlobalSetting("ADD_CORP_TICKER", "false") == "true";

		// Prepare Members
		$esamMembers = new EsamMembers();
		// Prepare Alliance
		$esamAlliance = new EsamAlliance();

		// Load the rules
		$esamRules = new EsamRules();
		$rules = $esamRules->listRules();

		if (sizeof($resultSet) == 0)
		{
			debug(0, "No API(s) to validate! <br />");
		}
		if (sizeof($rules) == 0)
		{
			debug(0, "No Rules to validate!  <br />");
		}

		// Get a full list of all the MemberGroups that have
		// a rule defined for them
		$memberGroupsWithRules = array();
		foreach ($rules as $row)
		{
			$memberGroupsWithRules[] = $row['GROUP_ID'];
		}
		$memberGroupsWithRules = array_unique($memberGroupsWithRules);

		$startValidationWith = $this->esamGlobal->getGlobalSetting("START_VALIDATION_WITH", -1);

		foreach ($resultSet as $row)
		{
			$member_id = $row['ID_MEMBER'];
			$user_id = $row['USER_ID'];
			$api_key = $row['API_KEY'];

			$currentTime = $this->getTime();
			if ($multipleKeyAuthentication && ($currentTime - $startTime) > 20)
			{
			// We're taking too long, let the next iteration handle the rest
				$this->esamGlobal->setGlobalSetting("START_VALIDATION_WITH", $member_id);
				debug(1, "Nearing 30 second timeout, validation halted.<br />");
				debug(1, "Click <a href='$scripturl?action=EsamSettings;sa=validate'>here</a> to resume validation.<br />");
				debug(1, "Alternatively, the screen will refresh itself and resume validation automatically in 30 seconds.");
				debug(1, "<script>setTimeout('window.location = \'$scripturl?action=EsamSettings;sa=validate\';', 30000);</script>");

				return;
			}
			else if ($multipleKeyAuthentication && $member_id < $startValidationWith)
				{
					continue;
				}

			$valid = $this->checkUserAndKey($user_id, $api_key) ? "true" : "false";
			debug(1, "Member: <b>" . $esamMembers->getMemberName($member_id) . "</b><br />");
			debug(2, $user_id . ' ' . $api_key . ' ' . $valid . '<br />');

			// check to see if the user is still in the DB
			$esamQuery = new EsamQuery();
			$esamQuery->setQuery($esamQuery->Q_MEMBER_INFO);
			$esamQuery->setParameter("member_id", $member_id);
			$memberInfo = $esamQuery->getResultSet();
			if (sizeof($memberInfo) == 0 )
			{
				// TODO: clear esam information
			}

			// Clear member from MemberGroups that have rules
			$esamMembers->clearMemberFromMemberGroups($member_id, $memberGroupsWithRules);

			if ($valid == "false")
			{
			// Invalid API keys have no need to be kept around,
			// remove it and move on.
				$esamQuery = new EsamQuery();
				$esamQuery->setQuery($esamQuery->D_CLEAR_APIKEY);
				$esamQuery->setParameter("member_id", $member_id);
				$esamQuery->executeQuery();
			}
			else
			{
				$ea = new EsamEveApi();
				$ea->setCredentials($user_id, $api_key);
				$data = $ea->getCharacters();
				$xml = new SimpleXMLElement($data);
				debug(2, "<pre>" . str_replace(">", "&gt;", str_replace("<", "&lt;", $xml->asXML())) . "</pre>");

				$toon_names = array();

				foreach ($xml->result->rowset->row as $row)
				{
					foreach ($row->attributes() as $key => $val)
					{
						if($key == 'characterID')
						{
							$ea->setCredentials($user_id, $api_key, $val);
							$c = $ea->getCharacterSheet();
							if($ea->isError())
							{
								// debug(1, '<b>' . $esamMembers->getMemberName($member_id) . '</b><br />');
								debug(1, '<p style="color: red;">ERROR:<br />');
								debug(1, $ea->getErrors());
								debug(1, '</p>');
								debug(3, "<a href='http://api.eve-online.com/char/CharacterSheet.xml.aspx?userID=" . $user_id . "&apiKey=" . $api_key. "&characterID=" . $val . "'>character sheet</a><br />");
								debug(1, '<br /><br />');
							}
							else
							{
								$cxml = new SimpleXMLElement($c);
								$char_name = $cxml->result->name;
								$corp_id = $cxml->result->corporationID;
								$corp_name = $cxml->result->corporationName;
								$char_id = $cxml->result->characterID;

								$charIsDirector = false;
								$cresult = $cxml->xpath("//eveapi/result/rowset[@name='corporationRoles']");
								foreach ($cresult[0]->row as $crow)
								{
									foreach ($crow->attributes() as $ckey => $cval)
									{
										if($ckey == 'roleName' && $cval == 'roleDirector')
										{
											$charIsDirector = true;
										}
									}
								}

								debug(1, '<b>' . $char_name . '</b><br />');
								debug(1, 'Corp: ' . $corp_id . ' - ' . $corp_name . '<br />');
								debug(1, 'isDirector? ' . ($charIsDirector ? 'yes' : 'no') . '<br />');
								debug(3, "<a href='http://api.eve-online.com/char/CharacterSheet.xml.aspx?userID=" . $user_id . "&apiKey=" . $api_key. "&characterID=" . $char_id . "'>character sheet</a><br />");

								debug(2, "<pre>" . str_replace(">", "&gt;", str_replace("<", "&lt;", $cxml->asXML())) . "</pre>");

								// Iterate through all rules
								foreach ($rules as $rule)
								{
									$rule_type = $rule['TYPE'];
									$rule_requires_director_role = ($rule['ROLE'] == 'roleDirector');
									$rule_role_id = $rule['ROLE_ID'];
									$member_group = $rule['GROUP_ID']; // Member Group
									$member_group_name = $this->esamGlobal->getGroupName($member_group);
                  $skillTypeId = $rule['SKILL_ID'];
                  $skillLvl = $rule['SKILL_LVL'];

                  $skillRequired = false;
                  $hasSkill = false;
                  $skillDisplay = "";
                  if (!empty($skillTypeId) && !empty($skillLvl))
                  {
                    $skillRequired = true;
                    $skills = $cxml->result->rowset[0];
                    foreach ($skills as $skill)
                    {
                      if ( $skill["typeID"] == $skillTypeId && (int) $skill["typeID"] >= (int) $skillLvl)
                      {
                        $skillsArray = getSkillArray();
                        $skillDisplay = " and has the skill " . $skillsArray["$skillTypeId"] . " trained to at least " . $skillLvl;
                        $hasSkill = true;
                      }
                    }
                  }

                  if (!$skillRequired || ($skillRequired && $hasSkill))
                  {
                    switch($rule_type)
                    {
                      case 'P': // Pilot rules
                        if ((string) $char_id == (string) $rule_role_id)
                        {
                          $this->addMemberToGroup($member_id, $member_group);
                          $toon_names = $this->addUnique($toon_names, $char_name, $char_id, $corp_id);
                          debug(1, "Is this the pilot " . $esamAlliance->getCharacterName($rule_role_id));
                          debug(1, " - member qualifies, adding user to member group " . $member_group_name . " <br />" . $skillDisplay);
                        }
                        break;
                      case 'A': // Alliance rules
                        $rule_alliance_id = $rule_role_id; // only here for clarification
                        $isMemberCorp = false;
                        $alliance_info = $this->getAllianceXml()->xpath("//eveapi/result/rowset/row[@allianceID='" . $rule_alliance_id . "']/rowset");
                        $alliance_info_xml = $alliance_info[0];
                        $corps = $alliance_info_xml->xpath("./row/@corporationID");
                        foreach ($corps as $corp)
                        {
                          $corpInAllianceId = $corp->corporationID;
                          if ((string) $corpInAllianceId == (string) $corp_id)
                          {
                            $isMemberCorp = true;
                          }
                        }

                        if ($isMemberCorp && !$rule_requires_director_role)
                        {
                          $allianceName = $esamAlliance->getAllianceName($rule_role_id);
                          $this->addMemberToGroup($member_id, $member_group);
                          $toon_names = $this->addUnique($toon_names, $char_name, $char_id, $corp_id);
                          debug(1, "Is " . ( $rule_requires_director_role ? "<i>director</i> within corp of " : "<i>member</i> of corp of ") . " alliance: <i>" . $allianceName . "</i>" . $skillDisplay);
                          debug(1, " - member qualifies, adding user to member group " . $member_group_name . " <br />");
                        }
                        else if ($isMemberCorp && $rule_requires_director_role && $charIsDirector)
                          {
                            $allianceName = $esamAlliance->getAllianceName($rule_role_id);
                            $this->addMemberToGroup($member_id, $member_group);
                            $toon_names = $this->addUnique($toon_names, $char_name, $char_id, $corp_id);
                            debug(1, "Is " . ( $rule_requires_director_role ? "<i>director</i> within corp of " : "<i>member</i> of corp of ") . " alliance: <i>" . $allianceName . "</i>" . $skillDisplay);
                            debug(1, " - member qualifies, adding user to member group " . $member_group_name . " <br />");
                          }
                        break;
                      case 'C': // Corporation rules
                        $corporationName = $esamAlliance->getCorporationName($rule_role_id);
                        $rule_corp_id = $rule_role_id; // only here for clarification
                        if ($corp_id == $rule_corp_id && ( $charIsDirector || $charIsDirector == $rule_requires_director_role ) )
                        {
                          $this->addMemberToGroup($member_id, $member_group);
                          $toon_names = $this->addUnique($toon_names, $char_name, $char_id, $corp_id);
                          debug(1, "Is " . ( $rule_requires_director_role ? " <i>director</i> within " : " <i>member</i> of ") . " corporation: <i>" . $corporationName . '</i> - ' . $skillDisplay);
                          debug(1, " - member qualifies, adding user to member group " . $member_group_name . " <br />");
                        }
                        break;
                    }
                  }
                }
                
								debug(1, "<br />");
							}
						}
					}
				}

				if ( $this->esamGlobal->getGlobalSetting('DISPLAY_EVE_NAME', "false") == "true")
				{
				// Update the display name
					$name = "";
					if (sizeof($toon_names) > 0 )
					{
						$usersPreferredChar = $esamMembers->getMemberSetting($member_id, "PREFERRED_CHAR_ID", "all");

						$first = true;
						foreach (array_keys($toon_names) as $toon_id)
						{
							$currentName = $toon_names[$toon_id];
							if ($usersPreferredChar == "all" || (string) $toon_id == (string) $usersPreferredChar)
							{
								if (!$first) $name = $name . ", ";
								$first = false;
								$name = $name . $currentName;
							}
						}
					}
					if ($name == "")
					{
						$name = $esamMembers->getMemberName($member_id);
					}
					debug(1, "Assigning name: <b>" . $name . "</b>");

					$esamQuery = new EsamQuery();
					$esamQuery->setQuery($esamQuery->U_MEMBER_NAME);
					$esamQuery->setParameter("member_id", $member_id);
					$esamQuery->setParameter("real_name", mysql_escape_string($name));
					$esamQuery->executeQuery();
				}
			}
			debug(1, "<hr /><br />");
		}
		$this->esamGlobal->removeGlobalSetting("START_VALIDATION_WITH");
		debug(1, "<h3>Validation Complete</h3>");
	}

	function getTime()
	{
		$date = getdate();
		return $date[0];
	}

	function addMemberToGroup($member_id, $member_group)
	{
	// (-1, 0, 1, 3)
		if (!array_search($member_group, array(-1, 0, 1, 3)))
		{
			$esamMembers = new EsamMembers();
			$esamMembers->addMemberToMemberGroup($member_id, $member_group);
		}
	}

	/**
	 * Created method since array_unique() was not working properly.
	 *
	 * @param <type> $array
	 * @param <type> $value
	 * @return <type>
	 */
	function addUnique($array, $name, $char_id, $corp_id)
	{
		if ($this->includeCorpTicker)
		{
			$eveApiCorp = new EsamEveApi();
			$eveApiCorp->setCredentials(null, null, null, $corp_id);
			$corpInformation = $eveApiCorp->getCorporationInfo();
			$corpInformationXml = new SimpleXMLElement($corpInformation);
			$corpTicker = (string) $corpInformationXml->result->ticker;
			$name = "[" . $corpTicker . "] " . $name;
		}

		if (empty($array[(string) $char_id]))
		{
			$array[(String) $char_id] = $name;
		}
		return $array;
	}

	function checkMember($member_id)
	{
		$esamQuery = new EsamQuery();
		$esamQuery->setQuery($esamQuery->Q_EVE_API);
		$esamQuery->setParameter("member_id", $member_id);
		$resultSet = $esamQuery->getResultSet();

		if (sizeof($resultSet) == 0 )
		{
			return "false";
		}

		$row = $resultSet[0];
		$user_id = $row['USER_ID'];
		$api_key = $row['API_KEY'];

		return $this->checkUserAndKey($user_id, $api_key) ? "true" : "false";
	}

	/**
	 * Checks that the user_id and api key are valid.
	 * Returns true if it is, false otherwise.
	 *
	 * @param <type> $user_id
	 * @param <type> $api_key
	 * @return <type>
	 */
	function checkUserAndKey($user_id, $api_key)
	{
	// check user info
		$ea = new EsamEveApi();
		$ea->setCredentials($user_id, $api_key);
		$data = $ea->getCharacters();
		return !$ea->isError();
	}
}
?>
